﻿///////////////////////////////////////////////////////////////////////
// Name: MainForm.cs
// Author: B. Gian James <gian@BattleDroids.net>
// Description: The mainform source. Most everything gets executed
//              out of here.
//
// $Id: MainForm.cs,v 1.1.1.1.2.1 2009/08/30 17:55:17 gian Exp $
///////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;
using System.IO;
using System.Drawing.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace AVR_Terminal
{
    enum ConnectMode { Text, Hex, Binary }
    
    public partial class MainForm : Form
    {
        static private SerialPort   serialPort;
		static private Socket		socket;
		static private Socket		clientSocket;
		protected delegate void ServerUpdateConsoleDelegate(string text);
		Thread serverThread;

        // buffers for the different connection modes
        static private ConnectMode  connectionMode;
        static private String       buffer;
        static private byte[]       bBuffer;
        static private ArrayList    stringArray;
		static private bool			bReadToEndl = false;
		static private bool			bTCPClientConnected = false;

        static private string       prompt = "prompt> ";
        static public  PrivateFontCollection pfc;
        private const int    MAX_BUFF_SZ = 1024;

        public MainForm()
        {
            InitializeComponent();
            InitData();
            textInput.Focus();
        }

        public void InitData()
        {
            serialPort = new SerialPort();
            stringArray = new ArrayList();
            pfc = new PrivateFontCollection();
            bBuffer = new byte[MAX_BUFF_SZ];

            LoadResourceFont();

            toolConnectButton.Enabled = false;
            toolDisconnectButton.Enabled = false;
            connectionMode = ConnectMode.Text;

            toolBaud.ComboBox.DataSource = Program.baudRates;
            toolBaud.ComboBox.DisplayMember = "Name";
            toolBaud.ComboBox.ValueMember = "Baud";
			toolBaud.ComboBox.SelectedValue = Properties.Settings.Default.Baud;

            //toolBaud.SelectedItem = Properties.Settings.Default.Baud;
            //toolBaud.SelectedText = Properties.Settings.Default.Baud.ToString();
            //toolBaud.SelectedIndex = 3;

            rtextDisplay.Font = new Font(pfc.Families[0], 9);
			textInput.Font = new Font(pfc.Families [0], 9);

            if (Properties.Settings.Default.ConnectMode == "Text")
            {
                statusLabelMode.Text = "Text";
                statusLabelMode.Image = Properties.Resources.ConnectionText;
				toolMode.Image = Properties.Resources.ConnectionText;
			}
            else if (Properties.Settings.Default.ConnectMode == "Hex")
            {
                statusLabelMode.Text = "Hex";
                statusLabelMode.Image = Properties.Resources.ConnectionHex;
				toolMode.Image = Properties.Resources.ConnectionHex;
			}
            else
            {
                statusLabelMode.Text = "Binary";
                statusLabelMode.Image = Properties.Resources.ConnectionBinary;
				toolMode.Image = Properties.Resources.ConnectionBinary;
			}

            if (Properties.Settings.Default.User != "")
            {
                prompt = Properties.Settings.Default.User;
                if (Properties.Settings.Default.MCU != "")
                {
                    prompt += "@"; prompt += Properties.Settings.Default.MCU; prompt += " $> ";
                }
            } 

			comboCOMPort.Items.Clear();
            foreach (string s in SerialPort.GetPortNames())
            {
                comboCOMPort.Items.Add(s);
            }
            // If default com port is in list make it selected
            if (comboCOMPort.Items.Contains(Properties.Settings.Default.ComPort))
            {
                comboCOMPort.Text = Properties.Settings.Default.ComPort;
            }
            if (comboCOMPort.Items.Count == 0)
                MessageBox.Show("Couldn't find any COM ports!");


            ConfigSerialPort();

			toolIP.Text = Properties.Settings.Default.ServerIP;
			toolPort.Text = Properties.Settings.Default.ServerPort;

        }

        private void ConfigSerialPort()
        {
			serialPort.BaudRate = int.Parse(toolBaud.Text);
            serialPort.Parity = Properties.Settings.Default.SerialParity;
            serialPort.StopBits = Properties.Settings.Default.SerialStopBits;
            serialPort.DataBits = Properties.Settings.Default.SerialDataBits;
            serialPort.Handshake = System.IO.Ports.Handshake.None;
            //serialPort.NewLine = "\r";
            //serialPort.ReadTimeout = Properties.Settings.Default.SerialReadTimeout;
            //serialPort.ReceivedBytesThreshold = 1;

            if (Properties.Settings.Default.ComPort != "NA")
            {
                serialPort.PortName = Properties.Settings.Default.ComPort;
                toolConnectButton.Enabled = true;
            }
            else
                MessageBox.Show("To get started select your COM port and Baud on the toolbar and hit Connect AVR");
            serialPort.DataReceived += new SerialDataReceivedEventHandler(OnSerialDataReceived);

        }
        public void LoadResourceFont()
        {
            byte[] dejavufont = Properties.Resources.DejaVuMonoSans;
            pfc.AddFontFile(@"Resources\DejaVuMonoSans.ttf");
        }
        public static void OnSerialDataReceived(
        object sender, SerialDataReceivedEventArgs args)
        {
            if (connectionMode == ConnectMode.Text && serialPort.IsOpen)
            {
                try
                {
					if ( bReadToEndl )
					{
						while ( serialPort.BytesToRead != 0 )
						{
							buffer = serialPort.ReadLine();
							stringArray.Add(buffer);
						}
					}
					else
					{
						buffer = serialPort.ReadExisting();
						stringArray.Add(buffer);
					}
                    Program.mainForm.Invoke(new EventHandler(DoConsoleUpdate));
                }
                catch (TimeoutException)
                {
                    //MessageBox.Show("TimeoutException on ReadLine()");
                }
            }
            else if (connectionMode == ConnectMode.Hex)
            {

            }
            else if (connectionMode == ConnectMode.Binary)
            {

            }
        }
        public void SetStatusBarText(string msg)
        {
            statusLabelMain.Text = msg;
        }
        public void SetStatusLabel(string status)
        {
            if (status == "online")
            {
                statusLabelState.Image = imagesStatus.Images[0];
                statusLabelState.Text = "Online";
            }
            else if (status == "offline")
            {
                statusLabelState.Image = imagesStatus.Images[1];
                statusLabelState.Text = "Offline";
            }
            else if (status == "busy")
            {
                statusLabelState.Image = imagesStatus.Images[2];
                statusLabelState.Text = "Busy";
            }
            else
            {
                statusLabelState.Image = imagesStatus.Images[3];
                statusLabelState.Text = "Idle";
            }
        }
        private void OpenComPort(bool open)
        {
            if (open == true)
            {
                if (Properties.Settings.Default.ComPort == "NA")
                {
                    MessageBox.Show("Bad COM port settings");
                    return;
                }
                statusLabelMain.Text = "Connecting...";

                ConfigSerialPort();
				try
				{
					serialPort.Open();
				}
				catch ( IOException e )
				{
					MessageBox.Show("Problem opening serial port: " + e.Message);
				}

                toolConnectButton.Enabled = false;
                toolDisconnectButton.Enabled = true;
                statusLabelConnected.Text = "AVR";
                statusLabelConnected.Image = Properties.Resources.AVR;
                //SetStatusLabel("online");
                toolBaud.Enabled = false;
                comboCOMPort.Enabled = false;
            }
            else
            {
                statusLabelMain.Text = "Disconnecting...";
                serialPort.Close();
                toolConnectButton.Enabled = true;
                toolDisconnectButton.Enabled = false;
                statusLabelConnected.Text = "PC";
                statusLabelConnected.Image = Properties.Resources.computer_off;
                //SetStatusLabel("offline");
                toolBaud.Enabled = true;
                comboCOMPort.Enabled = true;
            }
        }
        static private void DoConsoleUpdate(object sender, EventArgs e)
        {
            if (connectionMode == ConnectMode.Text)
            {
				for ( int x = 0; x < stringArray.Count; x++ )
				{
/*					string line = ( (String)stringArray [x] ).Trim();
					if ( line.StartsWith(">>") )
					{
						Program.mainForm.textInput.Text = ( (String)stringArray [x] );
						Program.mainForm.textInput.Focus();
						Program.mainForm.textInput.SelectionStart = Program.mainForm.textInput.TextLength;
						selectionStart = Program.mainForm.textInput.SelectionStart;
						bQueryInProgress = true;
					}
					else if ( line.StartsWith("**") )
					{
						Program.mainForm.rtextDisplay.SelectionColor = Color.SkyBlue;
						Program.mainForm.rtextDisplay.AppendText((String)stringArray [x] + "\n");
						Program.mainForm.rtextDisplay.SelectionColor = Properties.Settings.Default.FontColor;
					}
					else if ( line.StartsWith("<<") )
					{
						Program.mainForm.rtextDisplay.SelectionColor = Color.Red;
						Program.mainForm.rtextDisplay.AppendText((String)stringArray [x] + "\n");
						Program.mainForm.rtextDisplay.SelectionColor = Properties.Settings.Default.FontColor;
					}
					else
					{ */
						if ( bTCPClientConnected )
						{
							Program.mainForm.SendToSocket(clientSocket, (String)stringArray [x]);
							Program.mainForm.rtextDisplay.AppendText((String)stringArray [x]);
						} else
							Program.mainForm.rtextDisplay.AppendText(( (String)stringArray [x] ));
						//Program.mainForm.rtextDisplay.AppendText(( (String)stringArray [x] ) + "\n");
					//}

					Program.mainForm.rtextDisplay.ScrollToCaret();

				}
				stringArray.Clear();
            }
        }
		private void ServerUpdateConsole(string text)
		{
			rtextDisplay.AppendText(text);
		}
        public static byte[] StrToByteArray(string str)
        {
            System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
            return encoding.GetBytes(str);
        }
		private void SendToSocket(Socket s, string msg)
		{
			s.Send(Encoding.BigEndianUnicode.GetBytes(msg));
		}
		public void HandleTCPClient(object sender, DoWorkEventArgs e)
		{
			Socket sock = (Socket)e.Argument;
			SendToSocket(sock,"\r\n\r\nConnected to BattleDroid.net AVR Telnet Gateway v1.0.\r\n");
			if ( serialPort.IsOpen )
			{
				SendToSocket(sock, "An AVR is connected to the serial port.\r\n");
				SendToSocket(sock, "Ready to accept commands. Type \"quit\" to disconnect.\r\n");
			}
			else
				SendToSocket(sock, "An AVR is NOT connected to the serial port.\r\n");
			bTCPClientConnected = true;

			string recv = string.Empty;
			while ( recv.Contains("quit") == false )
			{
				recv = string.Empty;		
				while ( true )
				{ 
					byte [] recvb = new byte [1024];
					int bytesRead = sock.Receive(recvb,SocketFlags.None);
					recv += Encoding.ASCII.GetString(recvb, 0, bytesRead);
					if ( recv.Contains("\n") )
						break;
				}
				//string buff = recv.Trim();
				Invoke(new ServerUpdateConsoleDelegate(ServerUpdateConsole), new object [] { "DEBUG>> recv from inet: \'" + recv + "\'\n" });
				if (serialPort.IsOpen)
					serialPort.Write(recv);
			}
			SendToSocket(sock,"Closing connection.\r\n");
			sock.Shutdown(SocketShutdown.Both);
			bTCPClientConnected = false;
		}
		public void ServerStart()
		{
			socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
			IPAddress ip;

			if ( toolIP.Text != "ANY" )
			{
				ip = IPAddress.Parse(toolIP.Text);
				socket.Bind(new IPEndPoint(ip, int.Parse(toolPort.Text)));
			}
			else
				socket.Bind(new IPEndPoint(IPAddress.Any, int.Parse(toolPort.Text)));

			socket.Listen(1);
			SetStatusBarText("Waiting connections.");
			while ( true )
			{
				Socket sock = socket.Accept();
				BackgroundWorker bw = new BackgroundWorker();
				bw.DoWork += HandleTCPClient;
				bw.RunWorkerAsync(sock);
				clientSocket = sock;
			}
		}

        #region Event Handlers
        private void menuPortCombo_TextChanged(object sender, EventArgs e)
        {
            serialPort.PortName = comboCOMPort.Text;
            Properties.Settings.Default.ComPort = comboCOMPort.Text;
            toolConnectButton.Enabled = true;
        }
        private void toolConnectButton_Click(object sender, EventArgs e)
        {
            if (comboCOMPort.Text != "NA")
                OpenComPort(true);

            // let's notify our avr to wake up
            statusLabelMain.Text = "Connected @ " + serialPort.BaudRate.ToString();

        }
        private void toolDisconnectButton_Click(object sender, EventArgs e)
        {
            OpenComPort(false);
            statusLabelMain.Text = "Disconnected";

        }
        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
			if ( serialPort.IsOpen )
				serialPort.Close();
			if ( socket.IsBound )
				socket.Close();

			Properties.Settings.Default.Save();
		}
        private void OnFormClosing(object sender, FormClosingEventArgs e)
        {
            if (serialPort.IsOpen)
                serialPort.Close();
			if ( socket != null && socket.IsBound )
				socket.Close();

            Properties.Settings.Default.Save();
        }
        private void MainForm_Load(object sender, EventArgs e)
        {

        }
        private void textInput_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == 0x0D)
            {
                //string choice = textInput.Text.Substring(0);
				e.Handled = true;
				if ( serialPort.IsOpen )
				{
					rtextDisplay.AppendText(textInput.Text + "\n");
					serialPort.Write(textInput.Text);
				}
				textInput.Text = string.Empty;
                //textInput.Text = prompt;
            }
        }
        private void toolMode_DropDownItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {
            if (e.ClickedItem.Text == "Text")
            {
                toolConnectHex.Checked = false;
                toolConnectBinary.Checked = false;
                Properties.Settings.Default.ConnectMode = "Text";
                statusLabelMode.Text = "Text";
                statusLabelMode.Image = Properties.Resources.ConnectionText;
				toolMode.Image = Properties.Resources.ConnectionText;
            }
            else if (e.ClickedItem.Text == "Hex")
            {
                toolConnectText.Checked = false;
                toolConnectBinary.Checked = false;
                Properties.Settings.Default.ConnectMode = "Hex";
                statusLabelMode.Text = "Hex";
                statusLabelMode.Image = Properties.Resources.ConnectionHex;
				toolMode.Image = Properties.Resources.ConnectionHex;
			}
            else
            {
                toolConnectText.Checked = false;
                toolConnectHex.Checked = false;
                Properties.Settings.Default.ConnectMode = "Binary";
                statusLabelMode.Text = "Binary";
                statusLabelMode.Image = Properties.Resources.ConnectionBinary;
				toolMode.Image = Properties.Resources.ConnectionBinary;
			}
        }
        private void toolPrintButton_Click(object sender, EventArgs e)
        {
            printDialog1.ShowDialog();
        }
        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (saveFileDialog1.ShowDialog() == DialogResult.OK)
            {
                rtextDisplay.SaveFile(saveFileDialog1.FileName);
            }

        }
        private void toolClearButton_Click(object sender, EventArgs e)
        {
            rtextDisplay.Text = "";
            //textInput.Text = prompt;
        }
        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Close();
        }
        private void toolConfigureButton_Click(object sender, EventArgs e)
        {
            ConfigTerminal dlg = new ConfigTerminal();

            if (dlg.ShowDialog() == DialogResult.OK)
            {
                Properties.Settings.Default.SerialDataBits = (int)dlg.numDataBit.Value;
                Properties.Settings.Default.SerialFlow = (System.IO.Ports.Handshake)dlg.comboStopBit.SelectedItem;
                Properties.Settings.Default.SerialParity = (Parity)dlg.comboParity.SelectedItem;
                Properties.Settings.Default.SerialStopBits = (StopBits)dlg.comboStopBit.SelectedItem;

				rtextDisplay.BackColor = Properties.Settings.Default.BackgroundColor;
				rtextDisplay.ForeColor = Properties.Settings.Default.FontColor;
            }
        }
        private void toolBaud_TextChanged(object sender, EventArgs e)
        {
            serialPort.BaudRate = ((BaudRate)(toolBaud.SelectedItem)).Baud;
            Properties.Settings.Default.Baud = serialPort.BaudRate;
           
        }
        private void printPreviewToolStripMenuItem_Click(object sender, EventArgs e)
        {
            printPreviewDialog1 = new PrintPreviewDialog();

            //printPreviewDialog1.Document = 

            if ( printPreviewDialog1.ShowDialog() == DialogResult.OK )
            {

            }
        }
		private void toolStartTCP_Click(object sender, EventArgs e)
		{
			serverThread = new Thread(new ThreadStart(ServerStart));
			toolStartTCP.Enabled = false;
			toolStopTCP.Enabled = true;
			serverThread.Start();
			SetStatusLabel("online"); 
		}
		private void toolStopTCP_Click(object sender, EventArgs e)
		{
			serverThread.Abort();
			toolStartTCP.Enabled = true;
			toolStopTCP.Enabled = false;
			SetStatusLabel("offline");
			socket.Close();
		}

        #endregion

    }
}
